package rahmath.sharif.docrypt;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;

import rahmath.sharif.docrypt.constant.DocryptConstants;
import rahmath.sharif.docrypt.util.AESCryptor;
import rahmath.sharif.docrypt.util.Docrypt;
import rahmath.sharif.docrypt.util.DocryptUtil;
import rahmath.sharif.docrypt.util.MimeUtil;
import android.app.Activity;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Explorer extends ListActivity implements DocryptInterface,
		DocryptConstants {
	private ArrayList<ExplorerListItem> listItems;
	private boolean directory;
	private boolean file;
	private boolean chooseAny;
	private boolean isDrive;
	private File selectedFile;
	private File currentDirectory;
	private int lastPosition;
	private View lastView;
	private File parent;
	private String docryptDrivePassword;
	private Properties fileNameMap;
	private Properties mimeMap;
	private ProgressDialog progressDialog;

	public ProgressDialog getProgressDialog() {
		return this.progressDialog;
	}

	public Activity getActivity() {
		return this;
	}

	@Override
	public void onCreate(Bundle bundle) {
		super.onCreate(bundle);
		setContentView(R.layout.file_browser);
		Bundle extras = getIntent().getExtras();
		String chooseType = extras.getString(INTENT_EXTRA_CHOOSE);
		if (FILE.equals(chooseType)) {
			file = true;
		} else if (DIRECTORY.equals(chooseType)) {
			directory = true;
		} else if (CHOOSE_ANY.equals(chooseType)) {
			chooseAny = true;
		}
		selectedFile = new File(extras.get(INTENT_EXTRA_FILE_PATH).toString());
		if (extras.getBoolean(INTENT_EXTRA_FILE_PATH_IS_GRAND_PARENT)) {
			parent = selectedFile;
		} else {
			parent = Environment.getExternalStorageDirectory();
		}
		String docryptDrive = extras.getString(INTENT_EXTRA_DOCRYPT_DRIVE);
		if (null != docryptDrive && docryptDrive.equals(DOCRYPT_DRIVE)) {
			isDrive = true;
			View btnSelect = findViewById(R.id.btnSelect);
			ViewGroup selectParent = (ViewGroup) btnSelect.getParent();
			selectParent.removeView(btnSelect);
			Button btnCancel = (Button) findViewById(R.id.btnCancel);
			btnCancel.setText("Lock Drive");
			docryptDrivePassword = extras.getString(DOCRYPT_DRIVE_PASSWORD);
			readProperties();
		}
		if (selectedFile.isDirectory()) {
			getFiles(selectedFile.listFiles());
			currentDirectory = selectedFile;
		} else {
			currentDirectory = selectedFile.getParentFile();
			getFiles(currentDirectory.listFiles());
		}
		setBrowserPath();
	}

	private void setBrowserPath() {
		String absolutePath = selectedFile.getAbsolutePath();
		String path = absolutePath;
		if (isDrive) {
			path = formulatePathString(absolutePath);
		}
		((TextView) findViewById(R.id.textViewCurrentPath)).setText(path);
	}

	private String formulatePathString(String absolutePath) {
		String fileName = null;
		String path = null;
		if (absolutePath.equals(DocryptUtil.getDocryptDir(DOCRYPT_DRIVE_FOLDER)
				.getAbsolutePath())) {
			return absolutePath;
		}
		if (null != (fileName = fileNameMap.getProperty(absolutePath))) {
			int index = absolutePath.lastIndexOf(FILE_SEPARATOR);
			path = formulatePathString(absolutePath.substring(0, index))
					+ FILE_SEPARATOR + fileName;
		}
		return path;
	}

	@Override
	protected void onPause() {
		if (isDrive) {
			persistProperties();
		}
		super.onPause();
	}

	@Override
	public void onBackPressed() {
		back(null);
	}

	/* This method will be called after onTouch method on ExplorerListAdapter */
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		ExplorerListItem explorerListItem = (ExplorerListItem) listItems
				.get(position);
		selectedFile = ((ExplorerListItem) listItems.get(position)).getIoFile();
		if (explorerListItem.isDigin()) {
			currentDirectory = selectedFile;
			getFiles(selectedFile.listFiles());
			setBrowserPath();
		} else {
			if (chooseAny) {
				explorerListItem
						.setSelected(explorerListItem.isSelected() ? false
								: true);
				getListView().getAdapter().getView(position, v, null);
			} else {
				if (lastPosition != -1) {
					listItems.get(lastPosition).setSelected(false);
					getListView().getAdapter().getView(lastPosition, lastView,
							null);
				}
				listItems.get(position).setSelected(true);
				getListView().getAdapter().getView(position, v, null);
				lastPosition = position;
				lastView = v;
			}
		}
	}

	private void getFiles(File[] files) {
		lastPosition = -1;
		listItems = new ArrayList<ExplorerListItem>();
		if (null != files) {
			for (File file : files) {
				boolean isFile = false;
				if (file.isFile()) {
					isFile = true;
				}
				String fileName = null;
				if (isDrive) {
					fileName = fileNameMap.getProperty(file.getAbsolutePath(),
							file.getName());
				} else {
					fileName = file.getName();
				}
				if (directory) {
					if (isFile) {
						continue;
					}
					listItems.add(new ExplorerListItem(file, fileName, file
							.isFile(), R.drawable.ic_action_directory));
					continue;
				}
				if (isFile) {
					listItems.add(new ExplorerListItem(file, fileName, file
							.isFile(), isDrive ? Integer.parseInt(mimeMap
							.getProperty(file.getAbsolutePath())) : MimeUtil
							.getDrawableForFile(file)));
				} else {
					listItems.add(new ExplorerListItem(file, fileName, file
							.isFile(), R.drawable.ic_action_directory));
				}
			}
		}
		setListAdapter(new ExplorerListAdapter(this, R.id.explorerListItem,
				listItems, chooseAny));
		if (directory && listItems.isEmpty()) {
			((TextView) findViewById(android.R.id.empty))
					.setText(getString(R.string.noMoreFolders));
		}
	}

	public void cancel(View view) {
		finish();
	}

	public void select(View view) {
		Intent resultIntent = new Intent();
		if (chooseAny) {
			ArrayList<File> selectedFiles = new ArrayList<File>();
			for (ExplorerListItem explorerListItem : listItems) {
				if (explorerListItem.isSelected()) {
					selectedFiles.add(explorerListItem.getIoFile());
				}
			}
			if (selectedFiles.isEmpty()) {
				Toast.makeText(getApplicationContext(),
						getString(R.string.selectAnyFileOrFolders),
						Toast.LENGTH_SHORT).show();
				return;
			} else {
				resultIntent.putExtra(INTENT_EXTRA_CHOOSEN_PATH, selectedFiles);
			}
		} else if (directory && selectedFile.isFile()) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.selectOnlyDirectories),
					Toast.LENGTH_SHORT).show();
			return;
		} else if (file && selectedFile.isDirectory()) {
			Toast.makeText(getApplicationContext(),
					getString(R.string.selectOnlyFiles), Toast.LENGTH_SHORT)
					.show();
			return;
		} else {// this must be directory or file
			resultIntent.putExtra(INTENT_EXTRA_CHOOSEN_PATH, selectedFile
					.getAbsoluteFile().getAbsolutePath());
		}
		setResult(Activity.RESULT_OK, resultIntent);
		finish();
	}

	public void back(View view) {
		if (parent.getAbsolutePath().equals(currentDirectory.getAbsolutePath())) {
			Toast.makeText(getApplicationContext(),
					"Cannot go beyond this directory.", Toast.LENGTH_SHORT)
					.show();
			return;
		}
		selectedFile = currentDirectory = currentDirectory.getParentFile();
		if (currentDirectory.isDirectory()) {
			getFiles(currentDirectory.listFiles());
			setBrowserPath();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		if (isDrive) {
			getMenuInflater().inflate(R.menu.file_browser, menu);
		}
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.addFile:
			Intent intentChooseFile = new Intent(Explorer.this, Explorer.class);
			intentChooseFile.putExtra(INTENT_EXTRA_CHOOSE, CHOOSE_ANY);
			try {
				intentChooseFile.putExtra(INTENT_EXTRA_FILE_PATH, Environment
						.getExternalStorageDirectory().getCanonicalPath());
			} catch (IOException e) {
				e.printStackTrace();
			}
			startActivityForResult(intentChooseFile, INTENT_ADD_TO_DRIVE);
			return true;
		case R.id.extractFile:
			Intent intentChooseFolder = new Intent(Explorer.this,
					Explorer.class);
			intentChooseFolder.putExtra(INTENT_EXTRA_CHOOSE, DIRECTORY);
			try {
				intentChooseFolder.putExtra(INTENT_EXTRA_FILE_PATH, Environment
						.getExternalStorageDirectory().getCanonicalPath());
			} catch (IOException e) {
				e.printStackTrace();
			}
			startActivityForResult(intentChooseFolder,
					INTENT_EXTRACT_FROM_DRIVE);
			return true;
		case R.id.deleteFile:
			final ArrayList<File> selectedFiles = new ArrayList<File>();
			for (ExplorerListItem explorerListItem : listItems) {
				if (explorerListItem.isSelected()) {
					selectedFiles.add(explorerListItem.getIoFile());
				}
			}
			if (selectedFiles.isEmpty()) {
				Toast.makeText(getApplicationContext(),
						getString(R.string.selectAnyFileOrFolders),
						Toast.LENGTH_SHORT).show();
			} else {
				final File[] files = new File[selectedFiles.size()];
				final ProgressDialog progressDialog = ProgressDialog.show(this,
						"", "Processing. Please wait...", true);
				new Thread(new Runnable() {
					public void run() {
						final boolean success = DocryptUtil.deleteFiles(
								selectedFiles.toArray(files),
								Explorer.this.fileNameMap,
								Explorer.this.mimeMap);
						Explorer.this.runOnUiThread(new Runnable() {
							public void run() {
								progressDialog.dismiss();
								if (!success) {
									Toast.makeText(
											Explorer.this,
											"Some of the files are not deleted.",
											Toast.LENGTH_SHORT).show();
								}
								Explorer.this.refresh();
							}
						});
					}
				}).start();
			}
			return true;
		case R.id.help:
			Intent helpActivity = new Intent(Explorer.this, HelpActivity.class);
			startActivity(helpActivity);
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		switch (requestCode) {
		case INTENT_ADD_TO_DRIVE:
			if (resultCode == Activity.RESULT_OK) {
				@SuppressWarnings("unchecked")
				ArrayList<File> selectedFiles = (ArrayList<File>) data
						.getExtras().getSerializable(INTENT_EXTRA_CHOOSEN_PATH);
				File[] fileArray = new File[selectedFiles.size()];
				final Docrypt docrypt = new Docrypt(this,
						this.docryptDrivePassword,
						selectedFiles.toArray(fileArray), currentDirectory,
						Cipher.ENCRYPT_MODE, this.fileNameMap, this.mimeMap);
				progressDialog = new ProgressDialog(this);
				progressDialog.setMessage(getString(R.string.encrypting));
				progressDialog.setIndeterminate(false);
				progressDialog.setMax(100);
				progressDialog.setCanceledOnTouchOutside(false);
				progressDialog
						.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
				progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
						"Cancel", new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int which) {
								docrypt.cancel(true);
								dialog.dismiss();
							}
						});
				progressDialog
						.setOnKeyListener(new DialogInterface.OnKeyListener() {
							public boolean onKey(DialogInterface dialog,
									int keyCode, KeyEvent event) {
								return true;
							}
						});
				docrypt.execute((String[]) null);
			}
			break;
		case INTENT_EXTRACT_FROM_DRIVE:
			if (resultCode == Activity.RESULT_OK) {
				String folder = data.getExtras().getString(
						INTENT_EXTRA_CHOOSEN_PATH);
				ArrayList<File> selectedFiles = new ArrayList<File>();
				for (ExplorerListItem explorerListItem : listItems) {
					if (explorerListItem.isSelected()) {
						selectedFiles.add(explorerListItem.getIoFile());
					}
				}
				if (selectedFiles.isEmpty()) {
					Toast.makeText(getApplicationContext(),
							getString(R.string.selectAnyFileOrFolders),
							Toast.LENGTH_SHORT).show();
				} else {
					File[] fileArray = new File[selectedFiles.size()];
					final Docrypt docrypt = new Docrypt(this,
							this.docryptDrivePassword,
							selectedFiles.toArray(fileArray), new File(folder),
							Cipher.DECRYPT_MODE, this.fileNameMap, this.mimeMap);
					progressDialog = new ProgressDialog(this);
					progressDialog.setMessage(getString(R.string.decrypting));
					progressDialog.setIndeterminate(false);
					progressDialog.setMax(100);
					progressDialog.setCanceledOnTouchOutside(false);
					progressDialog
							.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
					progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
							"Cancel", new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int which) {
									docrypt.cancel(true);
									dialog.dismiss();
								}
							});
					progressDialog
							.setOnKeyListener(new DialogInterface.OnKeyListener() {
								public boolean onKey(DialogInterface dialog,
										int keyCode, KeyEvent event) {
									return true;
								}
							});
					docrypt.execute((String[]) null);
				}
			}
			break;
		}
	}

	public void readProperties() {
		this.fileNameMap = new Properties();
		this.mimeMap = new Properties();
		try {
			Cipher decryptCipher = AESCryptor.getCipher(docryptDrivePassword,
					Cipher.DECRYPT_MODE);
			CipherInputStream cisFileNamMap = new CipherInputStream(
					new FileInputStream(new File(
							DocryptUtil.getDocryptDir(DOCRYPT),
							"FileNameMap.properties")), decryptCipher);
			this.fileNameMap.load(cisFileNamMap);
			cisFileNamMap.close();
			CipherInputStream cisMimeMap = new CipherInputStream(
					new FileInputStream(new File(
							DocryptUtil.getDocryptDir(DOCRYPT),
							"MimeMap.properties")), decryptCipher);
			this.mimeMap.load(cisMimeMap);
			cisMimeMap.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	public void persistProperties() {
		try {
			Cipher encryptCipher = AESCryptor.getCipher(docryptDrivePassword,
					Cipher.ENCRYPT_MODE);
			CipherOutputStream cosFileNameMap = new CipherOutputStream(
					new FileOutputStream(new File(
							DocryptUtil.getDocryptDir(DOCRYPT),
							"FileNameMap.properties")), encryptCipher);
			this.fileNameMap.store(cosFileNameMap, null);
			cosFileNameMap.close();
			CipherOutputStream cosMimeMap = new CipherOutputStream(
					new FileOutputStream(new File(
							DocryptUtil.getDocryptDir(DOCRYPT),
							"MimeMap.properties")), encryptCipher);
			this.mimeMap.store(cosMimeMap, null);
			cosMimeMap.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void refresh() {
		getFiles(currentDirectory.listFiles());
	}
}